package org.hxzon.workeasy.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.hxzon.project.Dao;
import org.hxzon.util.StringUtil;
import org.hxzon.util.cache.FifoCache;
import org.hxzon.workeasy.model.EasyFile;
import org.hxzon.workeasy.model.EasyTag;
import org.hxzon.workeasy.model.FileTag;
import org.hxzon.workeasy.service.EasyTagService;
import org.hxzon.workeasy.util.WorkEasyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.common.collect.Sets;

@Service
public class EasyTagServiceImpl implements EasyTagService {
    @Resource
    protected Dao dao;
    private FifoCache<String, EasyTag> tagNameCache = new FifoCache<String, EasyTag>(1000);
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    public EasyTag findByTagName(String tagName) {
        if (tagName == null || tagName.isEmpty()) {
            return null;
        }
        EasyTag result = tagNameCache.get(tagName);
        if (result == null) {
            tagName = StringUtil.escapeSql(tagName);
            List<EasyTag> list = dao.findList(EasyTag.class, " where tagName='" + tagName + "' ", -1, -1);
            if (list.isEmpty()) {
                return null;
            } else if (list.size() > 1) {
                logger.error("tag[{}] not unique", tagName);
            }
            result = list.get(0);
            tagNameCache.add(result.getTagName(), result);
        }
        return result;
    }

    public void saveFileAndTag(List<EasyFile> files, String tagString) {
        String[] tags = WorkEasyUtil.toTags(tagString);
        List<EasyTag> allTag = new ArrayList<EasyTag>();
        //add new tag
        for (String tag : tags) {
            EasyTag easyTag = findByTagName(tag);
            if (easyTag == null) {
                easyTag = new EasyTag();
                easyTag.setTagName(tag);
                dao.save(easyTag);
            }
            allTag.add(easyTag);
        }
        //save files
        for (EasyFile file : files) {
            file.setTagString(tagString);
        }
        dao.saveAll(files);
        //save FileTags
        for (EasyFile file : files) {
            for (EasyTag tag : allTag) {
                FileTag fileTag = new FileTag();
                fileTag.setEasyFile(file);
                fileTag.setEasyTag(tag);
                dao.save(fileTag);
            }
        }
    }

    public List<EasyFile> findFileByTag(String tagString, long first, long size) {
        if (tagString == null || tagString.isEmpty()) {
            return dao.findAll(EasyFile.class, first, size);
        }
        boolean intersection = tagString.startsWith("+");
        if (intersection) {
            tagString = tagString.substring(1);
        }
        String[] tagNames = WorkEasyUtil.toTags(tagString);
        if (tagNames.length == 1) {
            return findFileByTagName(tagNames[0], first, size);
        } else {
            return findFileByTagNames(tagNames, first, size, intersection);
        }
    }

    public long countFileByTag(String tagName) {
        if (tagName == null || tagName.isEmpty()) {
            return dao.countAll(EasyFile.class);
        }
        return findFileIdsByTagName(tagName).size();
    }

    private List<EasyFile> findFileByTagName(String tagName, long first, long size) {
        List<String> idList = findFileIdsByTagName(tagName);
        if (idList.isEmpty()) {
            return Collections.emptyList();
        } else {
            String ids = "";
            for (String id : idList) {
                ids += (",'" + id + "'");
            }
            ids = ids.substring(1);
            return dao.findList(EasyFile.class, " where p.id in (" + ids + ") ", -1, -1);
        }
    }

    private List<EasyFile> findFileByTagNames(String[] tagNames, long first, long size, boolean intersection) {
        Set<String> fileIds1 = Sets.newHashSet();
        fileIds1.addAll(findFileIdsByTagName(tagNames[0]));
        Set<String> allIds = Sets.newHashSet(fileIds1);
        for (int i = 1; i < tagNames.length; i++) {
            Set<String> fileIds2 = Sets.newHashSet(findFileIdsByTagName(tagNames[i]));
            //The returned view performs better when set1 is the smaller of the two sets.
            allIds = intersection ? Sets.intersection(fileIds2, allIds) : Sets.union(fileIds2, allIds);
        }
        //
        if (allIds.isEmpty()) {
            return Collections.emptyList();
        } else {
            String ids = "";
            for (String id : allIds) {
                ids += (",'" + id + "'");
            }
            ids = ids.substring(1);
            return dao.findList(EasyFile.class, " where p.id in (" + ids + ") ", -1, -1);
        }
    }

    private List<String> findFileIdsByTagName(String tagName) {
        EasyTag easyTag = findByTagName(tagName);
        if (easyTag == null) {
            return Collections.emptyList();
        }
        return (List<String>) dao.query("select fileId from FileTag where tagId='" + easyTag.getId() + "'");
    }

}
